package com.zang.test.dubbo.common.extension;

import com.alibaba.dubbo.common.extension.Activate;
import com.alibaba.dubbo.common.extension.Adaptive;
import com.alibaba.dubbo.common.extension.SPI;
import com.alibaba.dubbo.common.utils.ConcurrentHashSet;
import com.alibaba.dubbo.common.utils.Holder;
import com.alibaba.dubbo.common.utils.StringUtils;
import com.zang.test.dubbo.constant.annotation.Execute;
import lombok.extern.slf4j.Slf4j;


import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.regex.Pattern;

/**
 * //TODO
 *
 * 未完成
 * 模仿 dubbo 获取SPI实例
 *  —— 通过配置文件加载指定的实现类
 * @author Zhang Qiang
 * @date 2019/10/25 11:15
 */

@Slf4j
public class ExecuteLoader<T> {

    /**
     * 既定目录
     */
    private static final String SERVICES_DIRECTORY = "META-INF/services/";
    private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";
    private static final String DUBBO_INTERNAL_DIRECTORY = "META-INF/dubbo/internal/";
    private static final Pattern NAME_SEPARATOR = Pattern.compile("\\s*[,]+\\s*");
    private static final ConcurrentMap<Class<?>, ExecuteLoader<?>> EXECUTE_LOADERS = new ConcurrentHashMap();
    private static final ConcurrentMap<Class<?>, Object> EXECUTE_INSTANCES = new ConcurrentHashMap();
    private final Class<?> type;
    private final ExecuteFactory objectFactory;

    /**
     * 缓存
     */
    private final Holder<Map<String, Class<?>>> cachedClasses = new Holder();
    private final Holder<Object> cachedAdaptiveInstance = new Holder();
    private final Map<String, Activate> cachedActivates = new ConcurrentHashMap();
    private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap();
    private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap();
    private volatile Class<?> cachedAdaptiveClass = null;
    private String cachedDefaultName;
    private volatile Throwable createAdaptiveInstanceError;
    private Set<Class<?>> cachedWrapperClasses;
    private Map<String, IllegalStateException> exceptions = new ConcurrentHashMap();

    /**
     * 构造
     * ExecuteFactory IOC工厂
     * @param clazz
     * @return
     */
    public ExecuteLoader(Class<T> clazz){
        this.type = clazz;
        this.objectFactory = type == ExecuteFactory.class ? null : (ExecuteFactory)getExecuteLoader(ExecuteFactory.class).getAdaptiveExecute();
    }

    public static <T> ExecuteLoader<T> getExecuteLoader(Class<T> type){

        if (type == null) {
            throw new IllegalArgumentException("Execute type == null");
        } else if (!type.isInterface()) {
            throw new IllegalArgumentException("Execute type(" + type + ") is not interface!");
        } else if (!type.isAnnotationPresent(SPI.class)) {
            throw new IllegalArgumentException("Execute type(" + type + ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
        }

        ExecuteLoader<T> executeLoader = (ExecuteLoader<T>) EXECUTE_LOADERS.get(type);
        if (executeLoader == null){
            EXECUTE_LOADERS.putIfAbsent(type, new ExecuteLoader<>(type));
            executeLoader = (ExecuteLoader<T>) EXECUTE_LOADERS.get(type);
        }
        return executeLoader;
    }

    /**
     * 根据name获取实例
     *
     * @param name
     * @return
     */
    public T getExecute(String name){
        if (name == null || name.length() ==0) throw new IllegalArgumentException(" Execute name == null ");
        if ("true".equals(name)) {
            // 获取默认的拓展实现类
            //return getDefaultExecute();
        }
        Holder<Object> holder = cachedInstances.get(name);
        if (holder == null){
            cachedInstances.putIfAbsent(name, new Holder<Object>());
        }
        Object instance = holder.get();
        //双重检索
        if (instance == null){
            synchronized (holder){
                instance = holder.get();
                if (holder.get() == null){
                    instance = createExecute(name);
                    holder.set(instance);
                }
            }
        }
        return (T) instance;
    }

    /**
     * 创建实例
     *
     * @param name
     * @return
     */
    private T createExecute(String name){

        Class<?> clazz = getExecuteClasses().get(name);
        if (clazz == null){
            throw findException(name);
        }

        try {
            T instance = (T) EXECUTE_INSTANCES.get(clazz);
            if (instance == null) {
                EXECUTE_INSTANCES.putIfAbsent(clazz, clazz.newInstance());

            }

        } catch (Throwable e) {
            throw new IllegalStateException("");
        }

        return (T) new Object();
    }

    /**
     * 获取所有拓展类
     * 1、首先从缓存里获取，如果没有则加载并加载进缓存
     * 获取cachedClasses，如果不存在则通过loadExecuteClasses加载然后存入cachedClasses缓存
     * @return
     */
    private Map<String, Class<?>> getExecuteClasses(){
        Map<String, Class<?>> classMap = this.cachedClasses.get();
        //双重检索
        if (classMap == null){
            synchronized (this.cachedClasses){
                classMap = this.cachedClasses.get();
                if (classMap == null){
                    classMap = this.loadExecuteClasses();
                    this.cachedClasses.set(classMap);
                }
            }
        }
        return classMap;
    }

    /**
     * 加载拓展类
     * 1、解析SPI注解内容
     * 2、调用load方法加载指定文件夹内配置文件
     * @return
     */
    private Map<String, Class<?>> loadExecuteClasses(){
        final SPI defaultAnnotation = type.getAnnotation(SPI.class);
        if (defaultAnnotation != null) {
            //SPI注解内容分解 SPI注解内设置为默认名
            String v = defaultAnnotation.value().trim();
            if (v.length() > 0) {
                String[] names = NAME_SEPARATOR.split(v);
                if (names.length > 1){
                    throw new IllegalStateException(" SPI注解内容不合法 ");
                }
                if (names.length == 1){
                    cachedDefaultName = names[0];
                }
            }
        }

        Map<String, Class<?>> executeClasses = new HashMap<>();
        this.loadDirectory(executeClasses, SERVICES_DIRECTORY);
        this.loadDirectory(executeClasses, DUBBO_DIRECTORY);
        this.loadDirectory(executeClasses, DUBBO_INTERNAL_DIRECTORY);
        return executeClasses;
    }

    /**
     * 加载配置文件
     * 1、加载资源目录
     * 2、调用liadResource方法加载资源
     * @param executeClasses Hoder持有容器
     * @param dir 配置文件目录
     * @return
     */
    private void loadDirectory(Map<String, Class<?>> executeClasses, String dir){
        String fileName = dir + this.type.getName();
        ClassLoader classLoader = ExecuteLoader.class.getClassLoader();
        Enumeration enumeration = null;
        try {
            if (classLoader == null){
                    enumeration = ClassLoader.getSystemResources(fileName);
            } else {
                enumeration = classLoader.getResources(fileName);
            }
        } catch (Throwable t) {
            log.error("加载扩展类时发生异常 ( 接口: {}, 配置文件: {} ).", this.type, fileName );
        }

        if (enumeration != null) {
            while (enumeration.hasMoreElements()){
                java.net.URL resourceURL = (URL) enumeration.nextElement();
                this.loadResource(executeClasses, classLoader, resourceURL);
            }
        }
    }

    /**
     * 加载资源
     *
     * @param classes 存放容器
     * @param classLoader 类加载器
     * @param url url地址
     * @return
     */
    public void loadResource(Map<String, Class<?>> classes, ClassLoader classLoader, URL resourceUrl) {
        try {
            InputStreamReader in = new InputStreamReader(resourceUrl.openStream(), "utf-8");
            BufferedReader reader = new BufferedReader(in);
            try {
                String line;
                while ((line = reader.readLine())!= null){
                    //截取注释之前字符串
                    final int ci = line.indexOf("#");
                    if (ci >= 0){
                        line = line.substring(0, ci).trim();
                    }
                    //加载并保存缓存
                    if (line.length()>0){
                        String name = null;
                        //读取配置文件
                        int i = line.indexOf("=");
                        if (i > 0){
                            name = line.substring(0, i).trim();
                            line = line.substring(i+1).trim();
                        }
                        if (line.length()>0){
                            this.loadCached(classes, Class.forName(line, true, classLoader), resourceUrl, name);
                        }
                    }

                }
            } finally {
                if (in!=null) in.close();
                if (reader!=null) reader.close();
            }
        } catch (Throwable t) {
            log.error("加载 Execute class 异常...");
        }

    }

    /**
     * 加载缓存
     *
     * @param classes
     * @param clazz
     * @param resourceUrl
     * @param typeClassName
     * @return
     */
    private void loadCached(Map<String, Class<?>> classes, Class<?> clazz, URL resourceUrl, String typeClassName) throws NoSuchMethodException {

        //父子检测
        if (!type.isAssignableFrom(clazz)){
            throw new IllegalStateException(" 非其子类，其心必异 ！");
        }

        //Adaptive注解检测 cachedAdaptiveClass
        if (clazz.isAnnotationPresent(Adaptive.class)){
            if (cachedAdaptiveClass == null){
                cachedAdaptiveClass = clazz;
            } else if (! cachedAdaptiveClass.equals(clazz)){
                throw new IllegalStateException(" 注解发现一个以上的自适应类： " + this.cachedAdaptiveClass.getClass().getName() + " , " + clazz.getClass().getName());
            }

        //包装类检测 cachedWrapperClasses
        } else if (isWrapperClass(clazz)){
            Set<Class<?>> warppers = cachedWrapperClasses;
            if (warppers == null){
                cachedWrapperClasses = new ConcurrentHashSet<>();
                warppers = cachedWrapperClasses;
            }
            warppers.add(clazz);

        //拓展类 cachedNames
        } else {
            //等着抛异常吧
            clazz.getConstructor();
            if (typeClassName == null || typeClassName.length() == 0){
                typeClassName = this.findAnnotationName(clazz);
                if (typeClassName.length() == 0){
                    throw new IllegalStateException(" 这个异常一般不会出现，有类就会有默认名的 ");
                }
            }
            String[] names = NAME_SEPARATOR.split(typeClassName);
            if (typeClassName != null && typeClassName.length() > 0){
                Activate activate = clazz.getAnnotation(Activate.class);
                if (activate!=null)
                cachedActivates.put(names[0], activate);
            }
            for (String n : names){
                if (!cachedNames.containsKey(clazz)){
                    cachedNames.put(clazz, n);
                }
                Class<?> c = classes.get(n);
                if (c == null){
                    classes.put(n, clazz);
                } else if (c != clazz){
                    throw new IllegalStateException(" 文不对题，类名称对不上 ");
                }
            }

        }


    }

    /**
     * 检测构造器
     * 检测PUBLIC方法 Wrapper包装 type的包装器
     * @param clazz
     * @return
     */
    private boolean isWrapperClass(Class<?> clazz){
        try {
            clazz.getConstructor(this.type);
            return true;
        } catch (NoSuchMethodException e) {
            return false;
        }
    }

    /**
     * 获取定义的name，默认为类名小写：name.toLowerCase();
     * @param clazz
     * @return
     */
    private String findAnnotationName(Class<?> clazz){
        Execute execute = clazz.getAnnotation(Execute.class);
        if (execute == null){
            String name = clazz.getClass().getSimpleName();
            if (name.endsWith(this.type.getSimpleName())){
                name = name.substring(0, name.length() - this.type.getSimpleName().length());
            }
            return name.toLowerCase();
        }
        return execute.value();
    }

    /**
     * 创建自适应类
     * 获取IOC工厂
     * @return
     */
    public T getAdaptiveExecute(){
        Object instance = this.cachedAdaptiveInstance.get();
        if (instance == null) {
            if (this.createAdaptiveInstanceError!=null){
                throw new IllegalStateException(" 未能创建自适应实例 ");
            }

            Holder holder = this.cachedAdaptiveInstance;
            synchronized (this.cachedAdaptiveInstance) {
                instance = this.cachedAdaptiveInstance.get();
                if (instance == null) {
                    try {
                        instance = this.createAdaptiveExecute();
                        this.cachedAdaptiveInstance.set(instance);
                    } catch (Throwable v5) {
                        this.createAdaptiveInstanceError = v5;
                        throw new IllegalStateException(" 拒绝创建 Adaptive Instance " + v5.toString(), v5);
                    }
                }
            }
        }
        return (T) instance;
    }


    private T createAdaptiveExecute(){
        try {
            return this.injectExtension((T) this.getAdaptiveExecuteClass().newInstance());
        } catch (Exception e) {
            throw  new IllegalStateException("Can not create adaptive extension " + this.type + ", cause: " + e.getMessage(), e);
        }
    }

    private Class<?> getAdaptiveExecuteClass() {
        this.getExecuteClasses();
        return this.cachedAdaptiveClass != null ? this.cachedAdaptiveClass : (this.cachedAdaptiveClass = this.createAdaptiveExecuteOnClass());
    }

    private Class<?> createAdaptiveExecuteOnClass(){
        return new Object().getClass();
    }

    //TODO
    private T injectExtension(T instance){
        return instance;
    }


    private IllegalStateException findException(String name){
        Iterator var2 = this.exceptions.entrySet().iterator();
        Map.Entry entry;
        do {
            if (!var2.hasNext()) {
                StringBuilder buf = new StringBuilder("No such extension " + this.type.getName() + " by name " + name);
                int i = 1;
                Iterator var4 = this.exceptions.entrySet().iterator();

                while(var4.hasNext()) {
                    Map.Entry<String, IllegalStateException> e2 = (Map.Entry)var4.next();
                    if (i == 1) {
                        buf.append(", possible causes: ");
                    }

                    buf.append("\r\n(");
                    buf.append(i++);
                    buf.append(") ");
                    buf.append((String)e2.getKey());
                    buf.append(":\r\n");
                    buf.append(StringUtils.toString((Throwable)e2.getValue()));
                }

                return new IllegalStateException(buf.toString());
            }

            entry = (Map.Entry)var2.next();
        } while(!((String)entry.getKey()).toLowerCase().contains(name.toLowerCase()));
        return (IllegalStateException)entry.getValue();
    };

}
